package cn.xinfei.xdecision.data.core.config;

import cn.xinfei.xdecision.common.model.datax.enums.ExecutorSceneEnum;
import cn.xinfei.xdecision.data.thread.AsyncExecuteThreadPool;
import cn.xinfei.xdecision.data.thread.MdcTraceIdTaskDecorator;
import cn.xinfei.xdecision.prometheus.thread.PrometheusThreadPool;
import com.alibaba.ttl.threadpool.TtlExecutors;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
@ConfigurationProperties(prefix = "xdecision.data.thread")
@Data
public class ThreadPoolConfig {


    private int defaultCorePoolSize = 10;
    private int defaultMaxPoolSize = 100;
    private int defaultQueueCapacity = 10000;
    private int defaultKeepAliveSeconds = 30;
    private int defaultWaitMillis = 30000;

    private HttpStandardThreadConfig httpStandardThreadConfig;
    private HttpVirutalThreadConfig httpVirutalThreadConfig;
    private GroovyThreadConfig groovyThreadConfig;
    private VarCacheThreadConfig varCacheThreadConfig;

    private Map<String, String> THREAD_CONFIG_MAP;

    public void setThreadConfigMap(Map<String, String> threadConfigMap) {
        THREAD_CONFIG_MAP = threadConfigMap;
    }

    public Map<String, String> getThreadConfigMap() {
        return THREAD_CONFIG_MAP;
    }

    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(defaultCorePoolSize);
        taskExecutor.setMaxPoolSize(defaultMaxPoolSize);
        taskExecutor.setQueueCapacity(defaultQueueCapacity);
        taskExecutor.setKeepAliveSeconds(defaultKeepAliveSeconds);
        taskExecutor.setThreadNamePrefix("Task-Executor-");
        taskExecutor.setTaskDecorator(new MdcTraceIdTaskDecorator(true));
        //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.initialize();
        return taskExecutor;
    }


    @Bean(name = "varCacheProviderThreadPoolExecutor")
    public ThreadPoolExecutor varCacheProviderThreadPoolExecutor() {
        AsyncExecuteThreadPool asyncExecuteThreadPool = new AsyncExecuteThreadPool(
                varCacheThreadConfig.getCorePoolSize(),
                varCacheThreadConfig.getMaxPoolSize(),
                varCacheThreadConfig.getKeepAliveSeconds(),
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(varCacheThreadConfig.getQueueCapacity()),
                "var-cache-provider-executor-thread-%d",
                new ThreadPoolExecutor.CallerRunsPolicy(),
                true,
                ExecutorSceneEnum.CACHE);
        return asyncExecuteThreadPool.getExecutorService();
    }

    @Bean(name = "virtualThreadPoolExecutor")
    public ExecutorService virtualThreadPoolExecutor() {
        AsyncExecuteThreadPool asyncExecuteThreadPool = new AsyncExecuteThreadPool(
                httpVirutalThreadConfig.getCorePoolSize(),
                httpVirutalThreadConfig.getMaxPoolSize(),
                httpVirutalThreadConfig.getKeepAliveSeconds(),
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(httpVirutalThreadConfig.getQueueCapacity()),
                "http-virtual-executor-thread-%d",
                new ThreadPoolExecutor.CallerRunsPolicy(),
                true,
                ExecutorSceneEnum.HTTPVIRTUAL);
        return TtlExecutors.getTtlExecutorService(asyncExecuteThreadPool.getExecutorService());
    }

    @Bean(name = "standardThreadPoolExecutor")
    public ExecutorService standardThreadPoolExecutor() {
        AsyncExecuteThreadPool asyncExecuteThreadPool = new AsyncExecuteThreadPool(
                httpStandardThreadConfig.getCorePoolSize(),
                httpStandardThreadConfig.getMaxPoolSize(),
                httpStandardThreadConfig.getKeepAliveSeconds(),
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(httpStandardThreadConfig.getQueueCapacity()),
                "standard-executor-thread-%d",
                new ThreadPoolExecutor.CallerRunsPolicy(),
                true,
                ExecutorSceneEnum.HTTPSTAND);
        return TtlExecutors.getTtlExecutorService(asyncExecuteThreadPool.getExecutorService());
    }

    @Bean(name = "groovyThreadPoolExecutor")
    public ExecutorService groovyThreadPoolExecutor() {
        AsyncExecuteThreadPool asyncExecuteThreadPool = new AsyncExecuteThreadPool(
                groovyThreadConfig.getCorePoolSize(),
                groovyThreadConfig.getMaxPoolSize(),
                groovyThreadConfig.getKeepAliveSeconds(),
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(groovyThreadConfig.getQueueCapacity()),
                "groovy-executor-thread-%d",
                new ThreadPoolExecutor.CallerRunsPolicy(),
                true,
                ExecutorSceneEnum.GROOVY);
        return TtlExecutors.getTtlExecutorService(asyncExecuteThreadPool.getExecutorService());
    }

    @Data
    public static class HttpStandardThreadConfig {
        private int corePoolSize = 5;
        private int maxPoolSize = 100;
        private int queueCapacity = 10000;
        private int keepAliveSeconds = 30;
    }

    @Data
    public static class HttpVirutalThreadConfig {
        private int corePoolSize = 5;
        private int maxPoolSize = 100;
        private int queueCapacity = 10000;
        private int keepAliveSeconds = 30;
    }

    @Data
    public static class GroovyThreadConfig {
        private int corePoolSize = 1;
        private int maxPoolSize = 100;
        private int queueCapacity = 10000;
        private int keepAliveSeconds = 30;
    }

    @Data
    public static class VarCacheThreadConfig {
        private int corePoolSize = 2;
        private int maxPoolSize = 5;
        private int queueCapacity = 100;
        private int keepAliveSeconds = 30;
    }


}
